import java.util.ArrayDeque;
import jdk.internal.vm.Continuation;
import jdk.internal.vm.ContinuationScope;

// java --enable-preview --add-opens java.base/jdk.internal.vm=ALL-UNNAMED Prime
public class Prime {
	static final ContinuationScope cs = new ContinuationScope("VirtualThreads");

	static class IntChan {
		private final ArrayDeque<Continuation> putQueue = new ArrayDeque<>();
		private final ArrayDeque<Continuation> getQueue = new ArrayDeque<>();
		private int value;
		private boolean hasValue;

		void put(int v) {
			while (hasValue) {
				putQueue.addLast(Continuation.getCurrentContinuation(cs));
				Continuation.yield(cs);
			}
			value = v;
			hasValue = true;
			var c = getQueue.pollFirst();
			if (c != null)
				c.run();
		}

		int get() {
			while (!hasValue) {
				getQueue.addLast(Continuation.getCurrentContinuation(cs));
				Continuation.yield(cs);
			}
			int v = value;
			hasValue = false;
			var c = putQueue.pollFirst();
			if (c != null)
				c.run();
			return v;
		}
	}

	static void go(Runnable r) {
		new Continuation(cs, r).run();
	}

	static void generate(IntChan ch) {
		//noinspection InfiniteLoopStatement
		for (int i = 2; ; i++)
			ch.put(i);
	}

	static void filter(IntChan in, IntChan out, int prime) {
		//noinspection InfiniteLoopStatement
		for (; ; ) {
			var i = in.get();
			if (i % prime != 0)
				out.put(i);
		}
	}

	public static void main(String[] args) {
		var count = args.length > 0 ? Integer.parseInt(args[0]) : 10000;
		go(() -> {
			var ch = new IntChan();
			var ch0 = ch;
			go(() -> generate(ch0));
			for (int i = 0; i < count; i++) {
				var prime = ch.get();
				// System.out.println(prime);
				var ch1 = ch;
				var ch2 = new IntChan();
				go(() -> filter(ch1, ch2, prime));
				ch = ch2;
			}
		});
	}
}
